package com.ruoyi.qywx.service;

import com.ruoyi.common.constant.ErrorConstants;
import com.ruoyi.common.core.cache.Meta;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.mybatis.Query;
import com.ruoyi.common.core.mybatis.Update;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.utils.CollectionUtils;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.NumberUtils;
import com.ruoyi.common.utils.StrUtils;
import com.ruoyi.qywx.callback.txl.ExtChatEncrypt;
import com.ruoyi.qywx.entity.ExtGroupChat;
import com.ruoyi.qywx.entity.ExtGroupChatMember;
import com.ruoyi.qywx.enums.ChatMemberType;
import com.ruoyi.qywx.getway.api.GroupChatApi;
import com.ruoyi.qywx.getway.entity.QyExtChatMember;
import com.ruoyi.qywx.getway.entity.QyExternalChat;
import com.ruoyi.qywx.getway.keys.QywxKey;
import com.ruoyi.qywx.mapper.ExtGroupChatMapper;
import com.ruoyi.qywx.mapper.ExtGroupChatMemberMapper;
import com.ruoyi.qywx.model.query.GroupChatQuery;
import com.ruoyi.qywx.model.vo.GroupChatDetailVo;
import com.ruoyi.qywx.model.vo.GroupChatManagerVo;
import com.ruoyi.qywx.service.inters.ContactService;
import com.ruoyi.qywx.service.inters.ExtGroupChatService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @author : hcl
 * @Date : 2023/9/7 10:53
 */
@Slf4j
@Service
public class ExtGroupChatServiceImpl implements ExtGroupChatService {
    @Resource
    private ExtGroupChatMapper groupChatMapper;
    @Resource
    private ExtGroupChatMemberMapper groupChatMemberMapper;
    @Autowired
    private ContactService contactService;
    @Autowired
    private RedisCache redisCache;

    @Override
    public List<GroupChatManagerVo> groupChatList(Long teamId, GroupChatQuery query) {
        return groupChatMapper.groupChatList(teamId, query);
    }

    @Override
    public AjaxResult groupChatDetail(Long teamId, Long groupChatId) {
        ExtGroupChat gchat = groupChatMapper.selectById(groupChatId);
        if (Objects.isNull(gchat) || !gchat.getTeamId().equals(teamId)) {
            return AjaxResult.error(ErrorConstants.NO_DATA);
        }

        Map<String, String> contactNameMap = contactService.contactNamesMap(
                Collections.singletonList(gchat.getUserId()), teamId
        );
        String nowStartDate = DateUtils.getDate();
        int dayJoin = groupChatMemberMapper.selectCount(new Query<ExtGroupChatMember>()
                .ge("join_time", nowStartDate)
                .eq("chat_id", gchat.getId()))
                .intValue();

        int dayLose = groupChatMemberMapper.selectCount(new Query<ExtGroupChatMember>()
                .isDelete(true)
                .ge("exit_time", nowStartDate)
                .eq("chat_id", gchat.getId()))
                .intValue();

        return AjaxResult.success(GroupChatDetailVo.builder()
                .name(gchat.getName())
                .createTime(gchat.getCreateTime())
                .owner(contactNameMap.getOrDefault(gchat.getUserId(), "未知"))
                .dayJoin(dayJoin)
                .dayLose(dayLose)
                .total(gchat.getTotal())
                .notice(gchat.getNotice())
                .externalTotal(groupChatMemberMapper.membersIsExternalCount(gchat.getId(), teamId))
                .build());
    }

    @Override
    public List<ExtGroupChatMember> groupChatMembers(Long teamId, Long chatId, String nickName, Boolean isDelete) {
        return groupChatMemberMapper.selectList(new Query<ExtGroupChatMember>()
                .teamId(teamId)
                .chatId(chatId)
                .isDelete(isDelete)
                .nonLike("name", nickName)
                .orderByAsc("is_delete")
                .orderByDesc("admin_type")
        );
    }

    @Override
    public AjaxResult groupChatBindTags(Long teamId, List<Long> ids, List<Long> tagIds) {
        return null;
    }

    @Override
    public void informCreateChat(Meta meta, String chatId) {
        QyExternalChat externalChat = GroupChatApi.getGroupChatDetail(meta, chatId);
        if (externalChat != null) {
            ExtGroupChat extGroupChat = externalChat.ofExtGroupChat(meta.getTeamId());
            insertGroupChat(extGroupChat);
            List<ExtGroupChatMember> chatMembers = externalChat.chatMembers(meta.getTeamId(), extGroupChat.getId());
            insertGroupChatMembers(chatMembers);
        }
    }

    @Transactional
    @Override
    public void insertGroupChat(ExtGroupChat chat) {
        groupChatMapper.insert(chat);
    }

    @Transactional
    @Override
    public void insertGroupChatMembers(List<ExtGroupChatMember> members) {
        if (CollectionUtils.isNotEmpty(members)) {
            groupChatMemberMapper.insertBatchSomeColumn(members);
        }
    }

    @Override
    public void addMember(Meta meta, ExtChatEncrypt encrypt) {
        QyExternalChat externalChat = GroupChatApi.getGroupChatDetail(meta, encrypt.getChatId());
        if (Objects.isNull(externalChat)) return;

        List<ExtGroupChatMember> exitMembers = findChatMembersByChatWxId(meta.getTeamId(),
                encrypt.getChatId(), true);
        if (CollectionUtils.isEmpty(exitMembers)) {
            log.info("未找到群成员数据,群wxId:{}", encrypt.getChatId());
            return;
        }
        Long chatId = exitMembers.get(0).getChatId();
        Map<String, ExtGroupChatMember> exitMemberMap = exitMembers.stream()
                .collect(Collectors.toMap(ExtGroupChatMember::getUserId, member -> member));
        if (CollectionUtils.isNotEmpty(externalChat.getMembers())) {
            List<ExtGroupChatMember> addNewMember = new ArrayList<>();
            externalChat.getMembers().forEach(m -> {
                ExtGroupChatMember member = exitMemberMap.getOrDefault(m.getUserid(), null);
                if (member == null) {
                    addNewMember.add(externalChat.chatMember(m, meta.getTeamId(), chatId));
                } else if (member.getIsDelete()) {
                    chatMemberReflux(member.getId(), m.getJoin_time());
                }
            });
            insertGroupChatMembers(addNewMember);
            // 更新群数量
            updateChatTotal(externalChat.getMembers().size(), chatId);
            // 更新群成员信息
            updateChatMemberName(exitMemberMap, meta.getTeamId(), chatId, externalChat.getMembers());
        }
    }


    @Transactional
    public void updateChatTotal(int total, Long chatId) {
        groupChatMapper.update(null, new Update<ExtGroupChat>()
                .set("total", total)
                .id(chatId)
        );
    }

    @Transactional
    public void chatMemberReflux(Long memberId, Long joinTime) {
        if (NumberUtils.isTrue(memberId)) {
            groupChatMemberMapper.update(null, new Update<ExtGroupChatMember>()
                    .setDelete(false)
                    .id(memberId)
                    .set("join_time", new Date(joinTime * 1000))
            );
        }
    }

    @Transactional
    @Override
    public void changeChatInfo(Meta meta, ExtChatEncrypt encrypt, String changeType) {
        QyExternalChat externalChat = GroupChatApi.getGroupChatDetail(meta, encrypt.getChatId());
        if (Objects.isNull(externalChat)) return;
        ExtGroupChat chat = groupChatMapper.selectOne(new Query<ExtGroupChat>()
                .teamId(meta.getTeamId())
                .wxId(externalChat.getChat_id()));
        Update<ExtGroupChat> update = new Update<ExtGroupChat>().id(chat.getId());
        if (changeType.equals("name")) {
            update.setName(externalChat.getName());
        } else if (changeType.equals("notice")) {
            update.set(changeType, externalChat.getNotice());
        } else if (changeType.equals("owner")) {
            update.setUserId(externalChat.getOwner());
            groupChatMemberMapper.update(null, new Update<ExtGroupChatMember>()
                    .set("admin_type", ChatMemberType.MANAGER.getCode())
                    .eq("chat_id", chat.getId())
                    .eq("admin_type", ChatMemberType.OWNER.getCode())
            );
            groupChatMemberMapper.update(null, new Update<ExtGroupChatMember>()
                    .set("admin_type", ChatMemberType.OWNER.getCode())
                    .userId(externalChat.getOwner())
                    .eq("chat_id", chat.getId())
            );
        }
        groupChatMapper.update(null, update);
    }

    @Override
    public void deleteMember(Meta meta, ExtChatEncrypt encrypt) {
        QyExternalChat externalChat = GroupChatApi.getGroupChatDetail(meta, encrypt.getChatId());
        if (Objects.isNull(externalChat)) return;
        List<ExtGroupChatMember> exitMembers = findChatMembersByChatWxId(meta.getTeamId(),
                encrypt.getChatId(), false);
        if (CollectionUtils.isEmpty(exitMembers)) {
            log.info("未找到群成员数据,群wxId:{}", encrypt.getChatId());
            return;
        }
        Map<String, ExtGroupChatMember> exitMemberMap = exitMembers.stream().collect(Collectors.toMap(ExtGroupChatMember::getUserId, member -> member));
        if (CollectionUtils.isNotEmpty(externalChat.getMembers())) {
            List<String> apiExitUserIds = externalChat.getMembers()
                    .stream()
                    .map(QyExtChatMember::getUserid)
                    .collect(Collectors.toList());

            List<String> delUserIds = exitMemberMap.keySet()
                    .stream()
                    .filter(key -> !apiExitUserIds.contains(key))
                    .collect(Collectors.toList());
            deleteGroupChatMembers(delUserIds, meta.getTeamId());
            // 更新群成员数量
            Long chatId = exitMembers.get(0).getChatId();
            updateChatTotal(externalChat.getMembers().size(), chatId);
            // 更新群成员信息
            updateChatMemberName(exitMemberMap, meta.getTeamId(), chatId, externalChat.getMembers());
        }
    }

    @Transactional
    public void deleteGroupChatMembers(List<String> delUserIds, Long teamId) {
        if (CollectionUtils.isNotEmpty(delUserIds)) {
            groupChatMemberMapper.update(null, new Update<ExtGroupChatMember>()
                    .setDelete(true)
                    .teamId(teamId)
                    .userId(delUserIds)
            );
        }
    }

    /**
     * 企业微信回调时，更新群成员昵称
     * 使用redis 防止入群、退群频繁
     *
     * @param exitMemberMap 已经存在的群成员 MAP => {userId : 昵称}
     * @param members       群详情返回的数据
     */
    @Transactional
    public void updateChatMemberName(Map<String, ExtGroupChatMember> exitMemberMap, Long teamId, Long chatId, List<QyExtChatMember> members) {
        String key = QywxKey.RedisKey.groupChatUpMembers(teamId, chatId);
        String cache = redisCache.getCacheObject(key);
        if (StrUtils.isEmpty(cache)) {
            redisCache.setCacheObject(key, "1", 30, TimeUnit.MINUTES);
            members.stream()
                    .filter(m -> {
                        ExtGroupChatMember member = exitMemberMap.getOrDefault(m.getUserid(), null);
                        return member != null && !member.getName().equals(m.getGroup_nickname());
                    })
                    .forEach(m -> groupChatMemberMapper.update(null, new Update<ExtGroupChatMember>()
                            .setName(m.getGroup_nickname())
                            .teamId(teamId)
                            .userId(m.getUserid())
                    ));
        }
    }

    /**
     * @param includeDelete true 包含流失成员
     */
    @Override
    public List<ExtGroupChatMember> findChatMembersByChatWxId(Long teamId,
                                                              String chatWxId,
                                                              boolean includeDelete) {
        Long chatId = groupChatMapper.findId(new Query<ExtGroupChat>()
                .teamId(teamId)
                .wxId(chatWxId)
        );
        Query<ExtGroupChatMember> query = new Query<>();
        query.eq("chat_id", chatId);
        if (!includeDelete) {
            query.isDelete(false);
        }
        return Objects.isNull(chatId) ? null : groupChatMemberMapper.selectList(query);
    }
}
